/*
  S.M.A.C.K - An operating system kernel
  Copyright (C) 2010,2011 Mattias Holm and Kristian Rietveld
  For licensing and a full list of authors of the kernel, see the files
  COPYING and AUTHORS.
*/

#ifndef KERNEL_PROCESS_H
#define KERNEL_PROCESS_H

typedef struct _proc_t proc_t;

#include <arch-process.h>
#include <list.h>
#include <vm.h>
#include <vfs/vfs.h>

#define PROC_TIME_QUANTA 10

typedef enum {
  PROC_STOPPED = 0,
  PROC_IDLE,
  PROC_RUNNING,
  PROC_BLOCKED,
} proc_state_t;

struct _proc_t {
  /* cpu_ctxt and cpu_helper CANNOT be moved! Assembly code relies on these
   * being at the start of the structure.
   */
  cpu_ctxt_t cpu_ctxt;
  cpu_helper_t cpu_helper;

  unsigned time_quanta; //!< When 0, process should preempt
  uint32_t cpu_time; //!< Number of clock ticks that have passed while this process was active
  char *name; //!< Name of process, for debugging only
  proc_t *parent; //!< Parent process

  vfs_vnode_t *cwd;

  LIST_ENTRY(_proc_t) processes; //!< Entry for list of all processes
  LIST_ENTRY(_proc_t) sched; //!< The scheduling queue this process is part of

  proc_state_t state; //!< State is not used for signaling state anymore, it serves as debugging help only
  pid_t pid;
  vfs_file_t *fd[PROC_MAX_FILES]; //!< Open files
  vfs_file_t *directory[PROC_MAX_FILES]; //!< Open directories

  uint32_t entrypoint; //!< Execution start address
  int exit_code;

  vm_map_t vm_map; // User space vm mappings
};

typedef LIST_HEAD(_proc_t) proc_list_t;


proc_t* proc_current(void);

proc_t* proc_create(proc_t *parent);
int proc_load(proc_t *proc, const char *file);
proc_t *proc_dup(proc_t *p);
void proc_unload(proc_t *p);
void proc_unmap(proc_t *p);
void proc_exit(proc_t *p, int exit_code);
int proc_wait(proc_t *p, int *exit_code);

/*
  Decrement the process time quanta and preempt if 0.
  \param p The process to decrement the time quanta for. Normally the current process.
  \pre Must be called from an interrupt handler
 */
void proc_tick(proc_t *p);


void proc_yield(proc_t *p);
void proc_block(proc_list_t *block_list, proc_t *p);
void proc_resume(proc_list_t *block_list, proc_t *p);

/* Must be called from interrupt handler */
void proc_resume_and_preempt(proc_list_t *block_list, proc_t *p);

void proc_signal(proc_t *p, int sig);

void proc_start(void);
void proc_switch(proc_t *p);
void proc_preempt(proc_t *p);

void proc_enqueue(proc_t *p);
void proc_dequeue(proc_t *p);

/*! Get next process to schedule */
proc_t* proc_schedule(void);

void proc_schedule_and_switch();

void proc_unblock_list(proc_list_t *block_list);

void proc_sleep(proc_t *p, int seconds);
void proc_wake(proc_t *p);       /* Must be called from interrupt handler */

/* Bookkeeping of open files and directories */
int proc_opendir(proc_t *p, vfs_vnode_t *n);
void proc_closedir(proc_t *p, int handle);

int proc_open(proc_t *p, vfs_vnode_t *n);
void proc_close(proc_t *p, int handle);

int proc_chdir(proc_t *p, const char *directory);
void proc_getwd(proc_t *p, char *directory, size_t size);

void proc_fill_processes(void *processes);

/* Architecture-dependent functions */
int hw_proc_create(proc_t *p);
void hw_proc_create_user_stack(proc_t *p);
void hw_proc_set_argv(proc_t *p, const char *argv, size_t size);
int hw_proc_load(proc_t *p, const char *filename);
void hw_proc_dup(proc_t *dup, proc_t *p);
void hw_proc_unload(proc_t *p);
void hw_proc_preempt(proc_t *p);

#endif /* ! KERNEL_PROCESS_H */
